﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization;
using System.Xml;

namespace Presentation.Web.Core.Serialization
{
    public class DataContractBinarySerializer
    {
        private DataContractSerializer _Serializer;

        public DataContractBinarySerializer(Type type)
        {
            _Serializer = new DataContractSerializer(type);
        }

        public void Serialize(Stream serializationStream, object graph)
        {
            using (XmlDictionaryWriter writer =
                XmlDictionaryWriter.CreateBinaryWriter(serializationStream))
            {
                _Serializer.WriteObject(writer, graph);
            }
        }

        public object Deserialize(Stream serializationStream)
        {
            using (XmlDictionaryReader reader =
                XmlDictionaryReader.CreateBinaryReader(serializationStream, XmlDictionaryReaderQuotas.Max))
            {
                return _Serializer.ReadObject(reader);
            }
        }

        #region Static Methods

        private static Dictionary<Type, DataContractBinarySerializer> _CachedSerializers = new Dictionary<Type, DataContractBinarySerializer>();

        private static DataContractBinarySerializer GetSerializer(Type type)
        {
            DataContractBinarySerializer serializer = null;
            if (!_CachedSerializers.TryGetValue(type, out serializer))
            {
                lock (_CachedSerializers)
                {
                    if (!_CachedSerializers.TryGetValue(type, out serializer))
                    {
                        serializer = new DataContractBinarySerializer(type);
                        _CachedSerializers.Add(type, serializer);
                    }
                }
            }
            return serializer;
        }

        public static string SerializeToString(object graph)
        {
            DataContractBinarySerializer serializer = GetSerializer(graph.GetType());
            using (MemoryStream memoryStream = new MemoryStream())
            using (DeflateStream compressedStream = new DeflateStream(memoryStream, CompressionMode.Compress))
            {
                serializer.Serialize(compressedStream, graph);
                compressedStream.Close();
                byte[] bs = memoryStream.GetBuffer();
                return Convert.ToBase64String(bs);
            }
        }

        public static object DeserializeFromString(Type type, string encodedString)
        {
            DataContractBinarySerializer serializer = GetSerializer(type);
            byte[] bs = Convert.FromBase64String(encodedString);
            using (MemoryStream memoryStream = new MemoryStream(bs))
            using (DeflateStream compressedStream = new DeflateStream(memoryStream, CompressionMode.Decompress))
            {
                return serializer.Deserialize(compressedStream);
            }
        }

        #endregion Static Methods

    }
}